﻿using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ServiceModel;
using Autofac;
using Autofac.Integration.Wcf;
using Cubicle.Core.Config;
using Cubicle.Core.Hosting;
using Cubicle.Core.Interfaces;
using Cubicle.Core.Processors;
using Cubicle.Core.Shared;
using Cubicle.Excel.Shared;

namespace Cubicle.Excel.Hosting
{
    [Export]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class EngineManager : ClientManager<EngineProxy>
    {
        // Nlog
        private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();

        [Import] private CompositionContainer _container;

        private Processor<object> _marshalQueue;
        private const string MarshalKey = "Marshal";

        [ImportingConstructor]
        public EngineManager(CompositionContainer container, EngineConfig config)
        {
            _container = container;
            _marshalQueue = new QueueWrapper<object>(_container, config, MarshalKey);
        }

        public bool Create(string alias, string script = null)
        {
            try
            {
                if (Clients.ContainsKey(alias)) return false;
                var engine = _container.GetExportedValue<IEngine>();
                var proxy = new LocalProxy(alias, _marshalQueue, engine);
                Clients[alias] = proxy;

                // If this is the first one, make it the default
                if (Clients.Count == 1) MakeDefault(alias);

                Logger.Info("Created local Exocortex. Alias = {0}", alias);
                return true;
            } catch (Exception e)
            {
                Logger.ErrorException("Error creating local Engine.", e);
                return false;
            }
        }

        public bool CreateAppDomain(string alias, string script = null)
        {
            try
            {
                if (Clients.ContainsKey(alias)) return false;
                var proxy = new AppDomainProxy(alias, _marshalQueue);
                Add(alias, proxy);

                Logger.Info("Created AppDomain Exocortex. Alias = {0}", alias);
                return true;
            }
            catch (Exception e)
            {
                Logger.ErrorException(e, "Error creating AppDomain Engine.");
                return false;
            }
        }

        public bool Connect(string alias, string address)
        {
            try
            {
                var container = CreateClientContainer(address);
                var proxy = new RemoteProxy(alias, _marshalQueue, address, container);
                
                // Try to issue a command
                if (!TestConnection(proxy)) return false;

                Add(alias, proxy);

                Logger.Info("Connected to remote Exocortex. Alias = {0}, Address = {1}", alias, address);
                return true;
            } catch (Exception e)
            {
                Logger.ErrorException(String.Format("Error connecting to remote Engine. Alias = {0}, Address = {1}", alias, address), e);
                return false;
            }

        }

        #region Type Registration

        private bool TestConnection(EngineProxy proxy)
        {
            try
            {
                var tmp = proxy.Id;
                return true;
            } catch (Exception)
            {
                return false;
            }
        }

        private static IContainer CreateClientContainer(string address)
        {
            var builder = new ContainerBuilder();
            builder
                .Register(c => ConnectionManager.CreateCortexChannelFactory(address))
                .SingleInstance();

            builder
                .Register(c => c.Resolve<ChannelFactory<IEngine>>().CreateChannel())
                .UseWcfSafeRelease();

            return builder.Build();
        }

        #endregion

    }
}